<?php
//   +----------------------------------------------------------------------
//   | Copyright (c) 2015-2025 http://www.hdphp.cn All rights reserved.
//   | Licensed ( http://www.hdphp.cn/licenses/ )
//   | Author: Jack <sophia2152@qq.com>
//   | 官方网址: http://www.hdphp.cn
//   | 这不是一个自由软件！您只能在不用于商业目的的前提下对程序代码进行修改和使用。
//   | 任何企业和个人不允许对程序代码以任何形式任何目的再发布。
//   +----------------------------------------------------------------------
namespace hdphp\traits\dao;

use hdphp\services\CacheService;
use hdphp\utils\Tag;
use think\cache\TagSet;
use think\Container;
use think\facade\Log;
use think\Model;

/**
 * Trait：CacheDaoTrait
 * 创建人：Jack
 * 创建时间：2023/11/21 16:09
 * @package hdphp\traits\dao
 * @method Model getModel()
 * @method Model getPk()
 */
trait CacheDaoTrait
{
    /**
     * 注释：获取redis
     * 创建人：Jack
     * 创建时间：2023/11/21 16:21
     * @return object|null
     */
    private function getRedisConnect()
    {
        return CacheService::redisHandler()->handler();
    }

    /**
     * 注释：获取缓存
     * 创建人：Jack
     * 创建时间：2023/11/21 16:21
     * @return TagSet|\think\facade\Cache
     */
    private function getCacheHander()
    {
        return CacheService::redisHandler();
    }

    /**
     * 对外开放方法
     * @return TagSet|\think\facade\Cache
     * @author 等风来
     * @email  136327134@qq.com
     * @date   2022/11/10
     */
    public function cacheHander()
    {
        return $this->getCacheHander();
    }

    /**
     * 注释：缓存标签
     * 创建人：Jack
     * 创建时间：2023/11/21 16:22
     * @param $tag
     * @return Tag
     */
    public function cacheTag($tag = null)
    {
        $key = $this->cacheKey() . 'tag';
        $tag = $tag ? $key . ':' . $tag : $key;
        $redis = CacheService::redisHandler($tag);

        return new Tag($redis, $tag);
    }

    /**
     * 注释：总缓存数据量
     * 创建人：Jack
     * 创建时间：2023/11/21 16:22
     * @return mixed
     */
    public function cacheCount()
    {
        $cacheKey = $this->cacheKey();
        $rds = $this->getRedisConnect();

        return $rds->hLen($cacheKey . 'map');
    }

    /**
     * 注释：读取缓存全部数据
     * 创建人：Jack
     * 创建时间：2023/11/21 16:22
     * @param string $key
     * @return array
     */
    public function cacheList(string $key = '')
    {
        $cacheKey = $this->cacheKey() . $key;
        $rds = $this->getRedisConnect();
        $map = $rds->hGetAll($cacheKey . 'map');
        //key排序
        ksort($map);

        $list = array_values($map) ?: [];

        foreach ($list as $key => $item) {
            $list[$key] = $this->unserialize($item);
        }

        return $list;
    }

    /**
     * 注释：读取缓存分页数据
     * 创建人：Jack
     * 创建时间：2023/11/21 16:23
     * @param int    $page
     * @param int    $limit
     * @param string $key
     * @return array
     */
    public function cachePageData(int $page = 1, int $limit = 10, string $key = '')
    {
        $cacheKey = $this->cacheKey() . $key;
        $rds = $this->getRedisConnect();

        $page = max($page, 1);
        $limit = max($limit, 1);

        // 先读排序
        $pageList = $rds->zRangeByScore($cacheKey . 'page', ($page - 1) * $limit, ($page * $limit) - 1);

        // 再读数据
        $list = $rds->hMGet($cacheKey . 'map', $pageList) ?: [];

        if (is_array($list)) {
            $newList = [];
            foreach ($list as $value) {
                $newList[] = $this->unserialize($value);
            }
            $list = $newList;
        }

        $count = $rds->hLen($cacheKey . 'map');

        return compact('list', 'count');
    }

    /**
     * 注释：单个查询数据
     * 创建人：Jack
     * 创建时间：2023/11/21 16:23
     * @param $id
     * @return mixed|null
     */
    public function cacheInfoById($id)
    {
        $cacheKey = $this->cacheKey();
        $rds = $this->getRedisConnect();

        $value = $rds->hGet($cacheKey . 'map', $id);

        return $value === null ? null : $this->unserialize($value);
    }

    /**
     * 注释：批量查询数据
     * 创建人：Jack
     * 创建时间：2023/11/21 16:23
     * @param array $ids
     * @return array
     */
    public function cacheInfoByIds(array $ids)
    {
        $cacheKey = $this->cacheKey();
        $rds = $this->getRedisConnect();

        $arr = $rds->hMGet($cacheKey . 'map', $ids);
        if (is_array($arr)) {
            $newList = [];
            foreach ($arr as $key => $value) {
                $arr[$key] = $this->unserialize($value);
            }
            $arr = $newList;
        }

        return $arr;
    }

    /**
     * 注释：更新单个缓存
     * 创建人：Jack
     * 创建时间：2023/11/21 16:23
     * @param array $info
     * @param       $key
     * @return false
     */
    public function cacheUpdate(array $info, $key = null)
    {
        $pk = $this->getPk();
        if ((empty($info) || !isset($info[$pk])) && !$key) {
            return false;
        }
        $cacheKey = $this->cacheKey();
        $rds = $this->getRedisConnect();
        $key = $key ?: $info[$pk];
        return $rds->hSet($cacheKey . 'map', $key, $this->serialize($info));
    }

    /**
     * 注释：序列化数据
     * 创建人：Jack
     * 创建时间：2023/11/21 16:23
     * @param $value
     * @return mixed|string
     */
    private function serialize($value)
    {
        try {
            return serialize($value);
        } catch (\Throwable $e) {
            Log::error('序列化发生错误:' . $e->getMessage());
            return $value;
        }
    }

    /**
     * 注释：反序列化数据
     * 创建人：Jack
     * 创建时间：2023/11/21 16:24
     * @param $value
     * @return mixed
     */
    private function unserialize($value)
    {
        try {
            return unserialize($value);
        } catch (\Throwable $e) {
            Log::error('反序列化发生错误:' . $e->getMessage());
            return $value;
        }
    }

    /**
     * 注释：cacheSaveValue
     * 创建人：Jack
     * 创建时间：2023/11/21 16:24
     * @param int $id
     * @param     $field
     * @param     $value
     * @return false
     */
    public function cacheSaveValue(int $id, $field, $value = null)
    {
        $pk = $this->getPk();
        $info = $this->cacheInfoById($id);
        if (!$info) {
            $newInfo = $this->get($id);
            $info = $newInfo ? $newInfo->toArray() : [];
        }
        if (is_array($field)) {
            foreach ($field as $k => $v) {
                $info[$k] = $v;
            }
        } else {
            $info[$field] = $value;
        }

        $info[$pk] = $id;
        return $this->cacheUpdate($info);
    }

    /**
     * 注释：不存在则写入，存在则返回
     * 创建人：Jack
     * 创建时间：2023/11/21 16:24
     * @param               $key
     * @param callable|null $fn
     * @param               $default
     * @return mixed|null
     */
    public function cacheRemember($key, callable $fn = null, $default = null)
    {

        // 不开启数据缓存直接返回
        if (!app()->config->get('cache.is_data')) {

            if ($fn instanceof \Closure) {
                return Container::getInstance()->invokeFunction($fn);
            } else {
                return $default;
            }

        }

        $info = $this->cacheInfoById($key);

        if ((null === $info || false === $info) && is_callable($fn)) {

            // 读取数据库缓存
            $newInfo = $fn();

            if (null !== $newInfo) {
                // 缓存数据存在则更新
                $this->cacheUpdate($newInfo, $key);
            }

            $info = $newInfo;
        }

        return null !== $info ? $info : $default;
    }

    /**
     * 注释：批量更新缓存
     * 创建人：Jack
     * 创建时间：2023/11/21 16:25
     * @param array  $list
     * @param string $key
     * @return false
     */
    public function cacheUpdateList(array $list, string $key = '')
    {
        if (empty($list)) {
            return false;
        }
        $cacheKey = $this->cacheKey() . $key;
        $pk = $this->getPk();
        $rds = $this->getRedisConnect();
        $map = [];
        foreach ($list as $item) {
            if (empty($item) || !isset($item[$pk])) {
                continue;
            }
            $map[$item[$pk]] = $this->serialize($item);
        }
        return $rds->hMSet($cacheKey . 'map', $map);
    }

    /**
     * 注释：删除单条缓存
     * 创建人：Jack
     * 创建时间：2023/11/21 16:25
     * @param int $id
     * @return void
     */
    public function cacheDelById(int $id)
    {
        $cacheKey = $this->cacheKey();
        $rds = $this->getRedisConnect();
        $rds->hDel($cacheKey . 'map', $id);
        $rds->zRem($cacheKey . 'page', $id);
    }

    /**
     * 注释：批量删除缓存
     * 创建人：Jack
     * 创建时间：2023/11/21 16:25
     * @param array $ids
     * @return void
     */
    public function cacheDelByIds(array $ids)
    {
        $cacheKey = $this->cacheKey();
        $rds = $this->getRedisConnect();
        foreach ($ids as $id) {
            $rds->hDel($cacheKey . 'map', $id);
            $rds->zRem($cacheKey . 'page', $id);
        }
    }

    /**
     * 注释：创建缓存
     * 创建人：Jack
     * 创建时间：2023/11/21 16:25
     * @param array  $list
     * @param string $key
     * @return void
     */
    public function cacheCreate(array $list, string $key = '')
    {
        $pk = $this->getPk();
        $cacheKey = $this->cacheKey() . $key;

        $rds = $this->getRedisConnect();

        // 启动事务
        $rds->multi();

        // 删除旧数据
        $rds->del($cacheKey . 'map');
        $rds->del($cacheKey . 'page');
        // 组合数据
        $map = [];
        foreach ($list as $i => $item) {
            $map[$item[$pk]] = $item;
            // 存zset     排序
            $rds->zAdd($cacheKey . 'page', $i, $item[$pk]);
        }
        foreach ($map as $k => &$item) {
            $item = $this->serialize($item);
        }
        // 存hmset    数据
        $rds->hMSet($cacheKey . 'map', $map);

        // 执行事务
        $rds->exec();
    }

    protected function cacheKey()
    {
        return 'mc:' . $this->getModel()->getName() . ':';
    }

    /**
     * 注释：getCacheKey
     * 创建人：Jack
     * 创建时间：2023/11/21 16:25
     * @param $key
     * @return string
     */
    public function getCacheKey($key)
    {
        return $this->cacheKey() . $key;
    }

    /**
     * 注释：更新缓存
     * 创建人：Jack
     * 创建时间：2023/11/21 16:25
     * @param string   $key
     * @param          $value
     * @param int|null $expire
     * @return bool
     */
    public function cacheStrUpdate(string $key, $value, int $expire = null)
    {
        return $this->getCacheHander()->set($this->cacheKey() . 'str:' . $key, $value, $expire);
    }

    /**
     * 注释：获取缓存
     * 创建人：Jack
     * 创建时间：2023/11/21 16:25
     * @param string $key
     * @return mixed
     */
    public function cacheStrGet(string $key)
    {
        return $this->getCacheHander()->get($this->cacheKey() . 'str:' . $key);
    }

    /**
     * 注释：获取表缓存是否有数据
     * 创建人：Jack
     * 创建时间：2023/11/21 16:26
     * @return mixed
     */
    public function cacheStrTable()
    {
        return $this->getRedisConnect()->get($this->cacheKey());
    }

    /**
     * 注释：设置表缓存是否有数据
     * 创建人：Jack
     * 创建时间：2023/11/21 16:26
     * @param int $value
     * @return mixed
     */
    public function cacheStrSetTable(int $value = 1)
    {
        return $this->getRedisConnect()->set($this->cacheKey(), $value);
    }

    /**
     * 注释：删除缓存
     * 创建人：Jack
     * 创建时间：2023/11/21 16:26
     * @param string $key
     * @return bool
     */
    public function cacheStrDel(string $key)
    {
        return $this->getCacheHander()->delete($this->cacheKey() . 'str:' . $key);
    }

    /**
     * 注释：获取缓存,没有则写入缓存并返回
     * 创建人：Jack
     * 创建时间：2023/11/21 16:26
     * @param string        $key
     * @param callable|null $fn
     * @param int|null      $expire
     * @param               $default
     * @return mixed|null
     */
    public function cacheStrRemember(string $key, callable $fn = null, int $expire = null, $default = null)
    {

        // 不开启数据缓存直接返回
        if (!app()->config->get('cache.is_data')) {

            if ($fn instanceof \Closure) {
                return Container::getInstance()->invokeFunction($fn);
            } else {
                return $default;
            }
        }

        $value = $this->cacheStrGet($key);

        if ((null === $value || false === $value) && is_callable($fn)) {

            $newValue = $fn();

            if (null !== $newValue) {
                $this->cacheStrUpdate($key, $value, $expire);
            }

            $value = $newValue;
        }

        return null !== $value ? $value : $default;
    }
}